/*
 * $Id: SignalAgent.cpp 111 2010-11-30 09:04:14Z hyunghwan.chung $
 *
   Copyright 2005-2009 Chung, Hyung-Hwan.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an  BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

#include <xp/bas/SignalAgent.hpp>
#include <xp/bas/assert.h>
#include <xp/bas/memory.h>

#ifndef _WIN32
#	include <errno.h>
#endif

XP_BEGIN_NAMESPACE2 (xp, bas)

SignalAgent* SignalAgent::instance = XP_NULL;

SignalAgent::SignalAgent ()
{
	//
	// WARNING: Each application should have only one 
	//          instance of SignalAgent.
	//
	XP_ASSERT (SignalAgent::instance == XP_NULL);
	SignalAgent::instance = this;

	for (xp_size_t i = 0; i < xp_countof(mask); i++) mask[i] = 0;
}

SignalAgent::~SignalAgent ()
{
	SignalAgent::instance = XP_NULL;
}

int SignalAgent::ignoreSignal (int sig)
{
	if (sig >= NSIG) return -1;
	mask[sig] = 1;

#ifdef HAVE_SIGACTION
	struct sigaction sa;

	xp_memset (&sa, 0, sizeof(sa));
 			sa.sa_handler = SIG_IGN;
	sigemptyset (&sa.sa_mask);
	sa.sa_flags = 0;
	if (sigaction (sig, &sa, NULL) == -1) return -1;
#else
	if (xp_signal (sig, SIG_IGN) == SIG_ERR) return -1;
#endif

	return 0;
}

int SignalAgent::defaultifySignal (int sig)
{
	if (sig >= NSIG) return -1;
	mask[sig] = 2;

#ifdef HAVE_SIGACTION
	struct sigaction sa;

	xp_memset (&sa, 0, sizeof(sa));
 			sa.sa_handler = SIG_DFL;
	sigemptyset (&sa.sa_mask);
	sa.sa_flags = 0;
	if (sigaction (sig, &sa, NULL) == -1) return -1;
#else
	if (xp_signal (sig, SIG_DFL) == SIG_ERR) return -1;
#endif

	return 0;
}

int SignalAgent::manageSignal (int sig, bool restart)
{
	if (sig >= NSIG) return -1;
	mask[sig] = 3;

#ifdef HAVE_SIGACTION
	struct sigaction sa;

	xp_memset (&sa, 0, sizeof(sa));
	sa.sa_handler = perform_proc;
	sigemptyset (&sa.sa_mask);
	sa.sa_flags = 0;
	if (restart)
	{
	#ifdef SA_RESTART
		sa.sa_flags |= SA_RESTART;
	#endif
	}
	else
	{
	#ifdef SA_INTERRUPT
		sa.sa_flags |= SA_INTERRUPT;
	#endif
	}
	if (sigaction (sig, &sa, NULL) == -1) return -1;
#else
	if (xp_signal(sig, perform_proc) == SIG_ERR) return -1;
#endif

	return 0;
}

int SignalAgent::ignoreAllSignals ()
{
	int r = 0;
	for (int i = 0; i < NSIG; i++) 
	{
		if (ignoreSignal(i) == -1) r = -1;
	}
	return r;
}

int SignalAgent::defaultifyAllSignals ()
{
	int r = 0;
	for (int i = 0; i < NSIG; i++) 
	{
		if (defaultifySignal(i) == -1) r = -1;
	}
	return r;
}

int SignalAgent::manageAllSignals (bool restart)
{
	int r = 0;
	for (int i = 0; i < NSIG; i++) 
	{
		if (manageSignal(i,restart) == -1) r = -1;
	}
	return r;
}

int SignalAgent::reapplyAllSigActions (bool restart)
{
	int r = 0;


	for (int i = 0; i < NSIG; i++) 
	{
		if (mask[i] == 0) continue;

	#ifdef HAVE_SIGACTION
		struct sigaction sa;

		xp_memset (&sa, 0, sizeof(sa));
		sigemptyset (&sa.sa_mask);
		sa.sa_flags = 0;
	#endif

		if (mask[i] == 1) 
		{
		#ifdef HAVE_SIGACTION
	 		sa.sa_handler = SIG_IGN;
			if (sigaction (i, &sa, NULL) == -1) r = -1;
		#else
			if (xp_signal(i, SIG_IGN) == SIG_ERR) r = -1;
		#endif
		}
		else if (mask[i] == 2) 
		{
		#ifdef HAVE_SIGACTION
	 		sa.sa_handler = SIG_DFL;
			if (sigaction (i, &sa, NULL) == -1) r = -1;
		#else
			if (xp_signal(i, SIG_DFL) == SIG_ERR) r = -1;
		#endif
		}
		else 
		{
		#ifdef HAVE_SIGACTION
			if (restart)
			{
			#ifdef SA_RESTART
				sa.sa_flags |= SA_RESTART;
			#endif
			}
			else
			{
			#ifdef SA_INTERRUPT
				sa.sa_flags |= SA_INTERRUPT;
			#endif
			}
	 		sa.sa_handler = perform_proc;
			if (sigaction (i, &sa, NULL) == -1) r = -1;
		#else
			if (xp_signal(i, perform_proc) == SIG_ERR) r = -1;
		#endif
		}
	}

	return r;
}

void SignalAgent::perform_proc (int sig)
{
#ifdef HAVE_SIGACTION
#ifndef _WIN32
	int n = errno;
#endif

	SignalAgent::instance->perform_proc_two (sig);

#ifndef _WIN32
	errno = n;
#endif
#else
	xp_signal (sig, SIG_IGN);
	SignalAgent::instance->perform_proc_two (sig);
	xp_signal (sig, SignalAgent::perform_proc);
#endif
}

void SignalAgent::perform_proc_two (int sig)
{
	XP_ASSERT (mask[sig] != 0 && mask[sig] != 1 && mask[sig] != 2);
	this->handleSignal (sig);
}

XP_END_NAMESPACE2 (xp, bas)
